//
// Created by xuchao on 16-11-21.
//

#ifndef OKPG_OKPGRESULTSET_H
#define OKPG_OKPGRESULTSET_H

#include <iostream>
#include <postgresql/libpq-fe.h>
#include "OkPgUtils.h"
#include "folly/Conv.h"

namespace OKPg {

    struct DataCell {
    private:
        int rowIndex_;
        int columnIndex_;
        std::string columnName_;
        std::string cellValue_;
    public:
        DataCell(const int rowIndex, const int columnIndex, const std::string &columnName, const std::string &cellValue)
                :
                rowIndex_(rowIndex), columnIndex_(columnIndex), columnName_(columnName), cellValue_(cellValue) {

        }

        int rowIndex() const {
            return rowIndex_;
        }

        int columnIndex() const {
            return columnIndex_;
        }

        std::string columnName() const {
            return columnName_;
        }

        std::string value() const {
            return cellValue_;
        };

        std::string toString() const {
            return cellValue_;
        }
    };

    struct DataRow {
    private:
        int rowIndex_;
        int columnCount_;
        std::vector<DataCell> dataCell_;
    public:
        DataRow(const int rowIndex, const int columnCount) : rowIndex_(rowIndex), columnCount_(columnCount) {

        }

        const int addCell(DataCell cell) {
            dataCell_.push_back(cell);
            return dataCell_.size() - 1;
        }

        const int rowIndex() {

            return rowIndex_;
        }

        const DataCell operator[](const int i) {
            return dataCell_[i];
        }

        const DataCell operator[](const std::string &columnName) {
            const int i = checkColumnIndex(getColumnIndex(columnName), columnCount_);
            return dataCell_[i];
        }

        const DataCell cell(const int i) {
            return dataCell_[i];
        }

        const std::string getColumnName(const int columnIndex) {
            const int i = checkColumnIndex(columnIndex, columnCount_);
            return dataCell_[i].columnName();
        }

        const int getColumnIndex(const std::string &columnName) {
            int columnIndex = -1;
            for (std::vector<DataCell>::const_iterator cdi = dataCell_.begin();
                 cdi != dataCell_.end(); ++cdi) {
                if ((*cdi).columnName() == columnName) {
                    columnIndex = (*cdi).columnIndex();
                    break;
                }
            }
            if (columnIndex < 0) {
                throw OkPgException("Invilid column name!");
            }
            return columnIndex + 1;
        }

        const std::string getString(const int columnIndex) {
            const int i = checkColumnIndex(columnIndex, columnCount_);
            return dataCell_[i].value();
        }

        const std::string getStringByName(const std::string &columnName) {
            const int i = checkColumnIndex(getColumnIndex(columnName), columnCount_);
            return dataCell_[i].value();
        }

        const int getInt(const int columnIndex) {
            const int i = checkColumnIndex(columnIndex, columnCount_);
            return string2int(dataCell_[i].value());
        }

        const int getIntByName(const std::string columnName) {
            const int i = checkColumnIndex(getColumnIndex(columnName), columnCount_);
            return string2int(dataCell_[i].value());
        }

        const double getDouble(const int columnIndex) {
            const int i = checkColumnIndex(columnIndex, columnCount_);
            return string2double(dataCell_[i].value());
        }

        const double getDoubleByName(const std::string columnName) {
            const int i = checkColumnIndex(getColumnIndex(columnName), columnCount_);
            return string2double(dataCell_[i].value());
        }
    };

    struct DataTable {
    private:
        std::string tableName_;
        int rowCount_;
        int columnCount_;
        std::vector<DataRow> dataRow_;

    private:
        void fullTable(PGresult *result) {
            for (int i = 0; i < rowCount_; ++i) {
                DataRow row(i, columnCount_);
                for (int j = 0; j < columnCount_; ++j) {
                    DataCell cell(i, j, PQfname(result, j), PQgetvalue(result, i, j));
                    row.addCell(cell);
                }
                addRow(row);
            }

        }

    public:
        DataTable(PGresult *result, const std::string &tableName = "")
                : tableName_(tableName),
                  rowCount_(result ? PQntuples(result) : 0),
                  columnCount_(result ? PQnfields(result) : 0) {
            if (result) {
                fullTable(result);
            }
        }

        const int addRow(DataRow row) {
            dataRow_.push_back(row);
            return dataRow_.size() - 1;
        }

        const DataRow operator[](const int i) const {
            return dataRow_[i];
        }

        const int rowsCount() const {
            return rowCount_;
        }

        const int columnCount() const {
            return columnCount_;
        }

        void foreachDataRow(std::function<void(const DataRow row)> loopFunc) const {
            if (rowCount_ <= 0) {
                return;
            }

            for (int i = 0; i < rowCount_; ++i) {
                loopFunc(dataRow_[i]);
            }
        }
    };

    class OkPgResultSet {
    private:
        bool hasError_;

        std::string errMsg_;

        DataTable dataTable_;

        int resultInt_;

    public:
        const bool hasError();

        const std::string errMsg();

        const DataTable resultDatatable();

        const int resultInt();

    public:
        OkPgResultSet(PGresult *result);

        OkPgResultSet(const int resultInt);

        OkPgResultSet(const std::string &errMsg);

        virtual ~OkPgResultSet();

    };
}


#endif //OKPG_OKPGRESULTSET_H
